﻿// Decompiled with JetBrains decompiler
// Type: System.Web.Razor.Generator.CodeGeneratorContext
// Assembly: System.Web.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// MVID: 682513D6-F37E-48AC-9909-F3EE103CFF14
// Assembly location: D:\Project\Idea\无人机外网\uav\客户端工程\排查工具\bin\Release\System.Web.Razor.dll

using System.CodeDom;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Web.Razor.Parser.SyntaxTree;
using System.Web.Razor.Resources;
using System.Web.Razor.Text;
using System.Web.Razor.Utils;

namespace System.Web.Razor.Generator
{
  /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Represents the context of the code generator.</summary>
  public class CodeGeneratorContext
  {
    private const string DesignTimeHelperMethodName = "__RazorDesignTimeHelpers__";
    private int _nextDesignTimePragmaId = 1;
    private bool _expressionHelperVariableWriten;
    private CodeMemberMethod _designTimeHelperMethod;
    private CodeGeneratorContext.StatementBuffer _currentBuffer = new CodeGeneratorContext.StatementBuffer();

    private CodeGeneratorContext() => this.ExpressionRenderingMode = ExpressionRenderingMode.WriteToOutput;

    internal ExpressionRenderingMode ExpressionRenderingMode { get; set; }

    private Action<string, CodeLinePragma> StatementCollector { get; set; }

    private Func<CodeWriter> CodeWriterFactory { get; set; }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Gets or sets the source file.</summary>
    /// <returns>The source file.</returns>
    public string SourceFile { get; internal set; }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Gets or sets the code compile unit that will hold the program graph.</summary>
    /// <returns>The code compile unit that will hold the program graph.</returns>
    public CodeCompileUnit CompileUnit { get; internal set; }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Gets or sets the generated namespace declaration.</summary>
    /// <returns>The generated namespace declaration.</returns>
    public CodeNamespace Namespace { get; internal set; }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Gets or sets the generated class type declaration.</summary>
    /// <returns>The generated class type declaration.</returns>
    public CodeTypeDeclaration GeneratedClass { get; internal set; }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Gets or sets the Razor engine host.</summary>
    /// <returns>The Razor engine host.</returns>
    public RazorEngineHost Host { get; private set; }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Gets or sets the dictionary collection of generated code mapping.</summary>
    /// <returns>The dictionary collection of generated code mapping.</returns>
    public IDictionary<int, GeneratedCodeMapping> CodeMappings { get; private set; }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Gets or sets the name of text writer.</summary>
    /// <returns>The name of text writer.</returns>
    public string TargetWriterName { get; set; }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Gets or sets the generated member method.</summary>
    /// <returns>The generated member method.</returns>
    public CodeMemberMethod TargetMethod { get; set; }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Gets the current buffered statement.</summary>
    /// <returns>The current buffered statement.</returns>
    public string CurrentBufferedStatement => this._currentBuffer != null ? this._currentBuffer.Builder.ToString() : string.Empty;

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Creates a new instance of the <see cref="T:System.Web.Razor.Generator.CodeGeneratorContext" /> class.</summary>
    /// <returns>The newly created instance of the code generator context.</returns>
    /// <param name="host">The Razor engine host.</param>
    /// <param name="className">The class name for the generated class type declaration.</param>
    /// <param name="rootNamespace">The name for the generated namespace declaration.</param>
    /// <param name="sourceFile">The source file.</param>
    /// <param name="shouldGenerateLinePragmas">true to enable the generation of line pragmas; otherwise, false.</param>
    public static CodeGeneratorContext Create(
      RazorEngineHost host,
      string className,
      string rootNamespace,
      string sourceFile,
      bool shouldGenerateLinePragmas)
    {
      return CodeGeneratorContext.Create(host, (Func<CodeWriter>) null, className, rootNamespace, sourceFile, shouldGenerateLinePragmas);
    }

    internal static CodeGeneratorContext Create(
      RazorEngineHost host,
      Func<CodeWriter> writerFactory,
      string className,
      string rootNamespace,
      string sourceFile,
      bool shouldGenerateLinePragmas)
    {
      CodeGeneratorContext generatorContext1 = new CodeGeneratorContext();
      generatorContext1.Host = host;
      generatorContext1.CodeWriterFactory = writerFactory;
      generatorContext1.SourceFile = shouldGenerateLinePragmas ? sourceFile : (string) null;
      generatorContext1.CompileUnit = new CodeCompileUnit();
      generatorContext1.Namespace = new CodeNamespace(rootNamespace);
      generatorContext1.GeneratedClass = new CodeTypeDeclaration(className)
      {
        IsClass = true
      };
      CodeMemberMethod codeMemberMethod = new CodeMemberMethod();
      codeMemberMethod.Name = host.GeneratedClassContext.ExecuteMethodName;
      codeMemberMethod.Attributes = MemberAttributes.Public | MemberAttributes.Override;
      generatorContext1.TargetMethod = codeMemberMethod;
      generatorContext1.CodeMappings = (IDictionary<int, GeneratedCodeMapping>) new Dictionary<int, GeneratedCodeMapping>();
      CodeGeneratorContext generatorContext2 = generatorContext1;
      generatorContext2.CompileUnit.Namespaces.Add(generatorContext2.Namespace);
      generatorContext2.Namespace.Types.Add(generatorContext2.GeneratedClass);
      generatorContext2.GeneratedClass.Members.Add((CodeTypeMember) generatorContext2.TargetMethod);
      generatorContext2.Namespace.Imports.AddRange(host.NamespaceImports.Select<string, CodeNamespaceImport>((Func<string, CodeNamespaceImport>) (s => new CodeNamespaceImport(s))).ToArray<CodeNamespaceImport>());
      return generatorContext2;
    }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Adds a code statement that inserts the Razor design time helpers method in the specified code statement.</summary>
    /// <param name="statement">The code statement that receives the code insertion.</param>
    public void AddDesignTimeHelperStatement(CodeSnippetStatement statement)
    {
      if (this._designTimeHelperMethod == null)
      {
        CodeMemberMethod codeMemberMethod = new CodeMemberMethod();
        codeMemberMethod.Name = "__RazorDesignTimeHelpers__";
        codeMemberMethod.Attributes = MemberAttributes.Private;
        this._designTimeHelperMethod = codeMemberMethod;
        this._designTimeHelperMethod.Statements.Add((CodeStatement) new CodeSnippetStatement(this.BuildCodeString((Action<CodeWriter>) (cw => cw.WriteDisableUnusedFieldWarningPragma()))));
        this._designTimeHelperMethod.Statements.Add((CodeStatement) new CodeSnippetStatement(this.BuildCodeString((Action<CodeWriter>) (cw => cw.WriteRestoreUnusedFieldWarningPragma()))));
        this.GeneratedClass.Members.Insert(0, (CodeTypeMember) this._designTimeHelperMethod);
      }
      this._designTimeHelperMethod.Statements.Insert(this._designTimeHelperMethod.Statements.Count - 1, (CodeStatement) statement);
    }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Adds a new generated code mapping to the collection.</summary>
    /// <returns>The collection index of the newly added code mapping.</returns>
    /// <param name="sourceLocation">The source location of the generated code mapping.</param>
    /// <param name="generatedCodeStart">The code start of the generated code mapping.</param>
    /// <param name="generatedCodeLength">The length of the generated code mapping.</param>
    public int AddCodeMapping(
      SourceLocation sourceLocation,
      int generatedCodeStart,
      int generatedCodeLength)
    {
      if (generatedCodeStart == int.MaxValue)
        throw new ArgumentOutOfRangeException(nameof (generatedCodeStart));
      GeneratedCodeMapping generatedCodeMapping = new GeneratedCodeMapping(sourceLocation.AbsoluteIndex, sourceLocation.LineIndex + 1, sourceLocation.CharacterIndex + 1, generatedCodeStart + 1, generatedCodeLength);
      int key = this._nextDesignTimePragmaId++;
      this.CodeMappings[key] = generatedCodeMapping;
      return key;
    }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Generates the line pragma for the specified source.</summary>
    /// <returns>The line pragma for the specified source.</returns>
    /// <param name="target">The source span.</param>
    public CodeLinePragma GenerateLinePragma(Span target) => this.GenerateLinePragma(target, 0);

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Generates the line pragma for the source.</summary>
    /// <returns>The line pragma for the specified source.</returns>
    /// <param name="target">The source span.</param>
    /// <param name="generatedCodeStart">The start index of code.</param>
    public CodeLinePragma GenerateLinePragma(Span target, int generatedCodeStart) => this.GenerateLinePragma(target, generatedCodeStart, target.Content.Length);

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Generates the line pragma for the source.</summary>
    /// <returns>The line pragma for the specified source.</returns>
    /// <param name="target">The source span.</param>
    /// <param name="generatedCodeStart">The start index of code.</param>
    /// <param name="codeLength">The length of code.</param>
    public CodeLinePragma GenerateLinePragma(
      Span target,
      int generatedCodeStart,
      int codeLength)
    {
      return this.GenerateLinePragma(target.Start, generatedCodeStart, codeLength);
    }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Generates the line pragma for the source.</summary>
    /// <returns>The line pragma for the specified source.</returns>
    /// <param name="start">The source location.</param>
    /// <param name="generatedCodeStart">The start index of code.</param>
    /// <param name="codeLength">The length of code.</param>
    public CodeLinePragma GenerateLinePragma(
      SourceLocation start,
      int generatedCodeStart,
      int codeLength)
    {
      if (string.IsNullOrEmpty(this.SourceFile))
        return (CodeLinePragma) null;
      return this.Host.DesignTimeMode ? new CodeLinePragma(this.SourceFile, this.AddCodeMapping(start, generatedCodeStart, codeLength)) : new CodeLinePragma(this.SourceFile, start.LineIndex + 1);
    }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Appends the content of the span to the current buffered statement.</summary>
    /// <param name="sourceSpan">The source span whose content is to be added.</param>
    public void BufferStatementFragment(Span sourceSpan) => this.BufferStatementFragment(sourceSpan.Content, sourceSpan);

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Appends the specified fragment to the current buffered statement.</summary>
    /// <param name="fragment">The fragment to add.</param>
    public void BufferStatementFragment(string fragment) => this.BufferStatementFragment(fragment, (Span) null);

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Appends the specified fragment to the current buffered statement.</summary>
    /// <param name="fragment">The fragment to add.</param>
    /// <param name="sourceSpan">The source span for the <paramref name="fragment" />.</param>
    public void BufferStatementFragment(string fragment, Span sourceSpan)
    {
      if (sourceSpan != null && this._currentBuffer.LinePragmaSpan == null)
      {
        this._currentBuffer.LinePragmaSpan = sourceSpan;
        int length = this._currentBuffer.Builder.Length;
        if (this._currentBuffer.GeneratedCodeStart.HasValue)
          length = this._currentBuffer.GeneratedCodeStart.Value;
        string str = CodeGeneratorPaddingHelper.Pad(this.Host, this._currentBuffer.Builder.ToString(), sourceSpan, length, out int _);
        this._currentBuffer.GeneratedCodeStart = new int?(length + (str.Length - this._currentBuffer.Builder.Length));
        this._currentBuffer.Builder.Clear();
        this._currentBuffer.Builder.Append(str);
      }
      this._currentBuffer.Builder.Append(fragment);
    }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Marks the start of generated code.</summary>
    public void MarkStartOfGeneratedCode() => this._currentBuffer.MarkStart();

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Marks the end of generated code.</summary>
    public void MarkEndOfGeneratedCode() => this._currentBuffer.MarkEnd();

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Flushes the current buffered statement.</summary>
    public void FlushBufferedStatement()
    {
      if (this._currentBuffer.Builder.Length <= 0)
        return;
      CodeLinePragma pragma = (CodeLinePragma) null;
      if (this._currentBuffer.LinePragmaSpan != null)
      {
        int length = this._currentBuffer.Builder.Length;
        if (this._currentBuffer.GeneratedCodeStart.HasValue)
          length = this._currentBuffer.GeneratedCodeStart.Value;
        int codeLength = this._currentBuffer.Builder.Length - length;
        if (this._currentBuffer.CodeLength.HasValue)
          codeLength = this._currentBuffer.CodeLength.Value;
        pragma = this.GenerateLinePragma(this._currentBuffer.LinePragmaSpan, length, codeLength);
      }
      this.AddStatement(this._currentBuffer.Builder.ToString(), pragma);
      this._currentBuffer.Reset();
    }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Adds the specified code statement to the body of the target method.</summary>
    /// <param name="generatedCode">The code statement to add the target method.</param>
    public void AddStatement(string generatedCode) => this.AddStatement(generatedCode, (CodeLinePragma) null);

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Adds the specified code statement to the body of the target method.</summary>
    /// <param name="body">The code statement to add the target method.</param>
    /// <param name="pragma">The line pragma.</param>
    public void AddStatement(string body, CodeLinePragma pragma)
    {
      if (this.StatementCollector == null)
      {
        CodeStatementCollection statements = this.TargetMethod.Statements;
        CodeSnippetStatement snippetStatement = new CodeSnippetStatement(body);
        snippetStatement.LinePragma = pragma;
        statements.Add((CodeStatement) snippetStatement);
      }
      else
        this.StatementCollector(body, pragma);
    }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Adds the expression helper variable to the generated class if not yet added,</summary>
    public void EnsureExpressionHelperVariable()
    {
      if (this._expressionHelperVariableWriten)
        return;
      CodeTypeMemberCollection members = this.GeneratedClass.Members;
      CodeMemberField codeMemberField = new CodeMemberField(typeof (object), "__o");
      codeMemberField.Attributes = MemberAttributes.Static | MemberAttributes.Private;
      members.Insert(0, (CodeTypeMember) codeMemberField);
      this._expressionHelperVariableWriten = true;
    }

    /// <summary>Assigns a new statement collector and returns a disposable action that restores the old statement collector.</summary>
    /// <returns>A disposable action that restores the old statement collector.</returns>
    /// <param name="collector">The new statement collector.</param>
    public IDisposable ChangeStatementCollector(Action<string, CodeLinePragma> collector)
    {
      Action<string, CodeLinePragma> oldCollector = this.StatementCollector;
      this.StatementCollector = collector;
      return (IDisposable) new DisposableAction((Action) (() => this.StatementCollector = oldCollector));
    }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code. Adds a code statement for a context call on the specified method.</summary>
    /// <param name="contentSpan">The content span.</param>
    /// <param name="methodName">The name of the method to invoke a context call.</param>
    /// <param name="isLiteral">true to specify that the method parameter is literal; otherwise, false.</param>
    public void AddContextCall(Span contentSpan, string methodName, bool isLiteral) => this.AddStatement(this.BuildCodeString((Action<CodeWriter>) (cw =>
    {
      cw.WriteStartMethodInvoke(methodName);
      if (!string.IsNullOrEmpty(this.TargetWriterName))
      {
        cw.WriteSnippet(this.TargetWriterName);
        cw.WriteParameterSeparator();
      }
      cw.WriteStringLiteral(this.Host.InstrumentedSourceFilePath);
      cw.WriteParameterSeparator();
      cw.WriteSnippet(contentSpan.Start.AbsoluteIndex.ToString((IFormatProvider) CultureInfo.InvariantCulture));
      cw.WriteParameterSeparator();
      cw.WriteSnippet(contentSpan.Content.Length.ToString((IFormatProvider) CultureInfo.InvariantCulture));
      cw.WriteParameterSeparator();
      cw.WriteSnippet(isLiteral.ToString().ToLowerInvariant());
      cw.WriteEndMethodInvoke();
      cw.WriteEndStatement();
    })));

    internal CodeWriter CreateCodeWriter()
    {
      if (this.CodeWriterFactory == null)
        throw new InvalidOperationException(RazorResources.CreateCodeWriter_NoCodeWriter);
      return this.CodeWriterFactory();
    }

    internal string BuildCodeString(Action<CodeWriter> action)
    {
      using (CodeWriter codeWriter = this.CodeWriterFactory())
      {
        action(codeWriter);
        return codeWriter.Content;
      }
    }

    private class StatementBuffer
    {
      public StringBuilder Builder = new StringBuilder();
      public int? GeneratedCodeStart;
      public int? CodeLength;
      public Span LinePragmaSpan;

      public void Reset()
      {
        this.Builder.Clear();
        this.GeneratedCodeStart = new int?();
        this.CodeLength = new int?();
        this.LinePragmaSpan = (Span) null;
      }

      public void MarkStart() => this.GeneratedCodeStart = new int?(this.Builder.Length);

      public void MarkEnd()
      {
        int length = this.Builder.Length;
        int? generatedCodeStart = this.GeneratedCodeStart;
        this.CodeLength = generatedCodeStart.HasValue ? new int?(length - generatedCodeStart.GetValueOrDefault()) : new int?();
      }
    }
  }
}
